package com.xlh.service.course.impl;

import com.google.common.collect.*;
import com.xlh.dao.course.CourseExtendMapper;
import com.xlh.dao.course.DirectionMapper;
import com.xlh.dao.course.ext.CourseExtendMapperExt;
import com.xlh.dao.course.ext.DirectionMapperExt;
import com.xlh.dto.course.*;
import com.xlh.enums.SystemConfigEnum;
import com.xlh.enums.course.ChapterEnum;
import com.xlh.enums.course.ChapterTypeEnum;
import com.xlh.enums.course.CourseStatusEnum;
import com.xlh.exception.common.BadRequestException;
import com.xlh.exception.common.NotFoundException;
import com.xlh.param.course.CourseDirectionParam;
import com.xlh.param.course.DirectionParam;
import com.xlh.pojo.course.CourseExtend;
import com.xlh.pojo.course.CourseExtendExample;
import com.xlh.pojo.course.Direction;
import com.xlh.pojo.course.DirectionExample;
import com.xlh.pojo.system.Permission;
import com.xlh.pojo.system.SystemConfig;
import com.xlh.security.UserInfoHolder;
import com.xlh.service.course.ChapterService;
import com.xlh.service.course.CourseService;
import com.xlh.service.course.DirectionService;
import com.xlh.service.system.SystemConfigService;
import com.xlh.service.user.UserService;
import com.xlh.util.BeanUtil;
import com.xlh.util.ExcelsUtil;
import com.xlh.virtualization.constant.ContextTypeEnum;
import com.xlh.virtualization.service.ImageAllocationService;
import com.xlh.virtualization.vo.ImageAllocationTableVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 课程方向
 * <p>
 * Created by lx on 2019/12/25.
 */
@Service
public class DirectionServiceImpl implements DirectionService {

    @Autowired
    private DirectionMapper directionMapper;

    @Autowired
    private CourseExtendMapper courseExtendMapper;

    @Autowired
    private CourseExtendMapperExt courseExtendMapperExt;

    @Autowired
    private DirectionMapperExt directionMapperExt;

    @Autowired
    private SystemConfigService configService;

    @Autowired
    private CourseService courseService;

    @Autowired
    private ChapterService chapterService;

    @Autowired
    private ImageAllocationService imageAllocationService;

    @Autowired
    private UserService userService;

    @Autowired
    private UserInfoHolder userInfoHolder;

    @Override
    public List<ZtreeDTO> listDirection(Integer type) {

        // 获取学校类型
        List<SystemConfig> configs = configService.listConfig(Lists.newArrayList(type), SystemConfigEnum.SCHOOL_TYPE);
        if (CollectionUtils.isEmpty(configs)) throw new BadRequestException();

        // 获取专业方向信息
        List<Direction> directions = queryDirection(Lists.newArrayList(type), null);
        if (CollectionUtils.isEmpty(directions)) return Lists.newArrayList();

        // 获取课程与专业方向的关联关系
        List<CourseExtend> courseExtends = queryCourseExtend(
                directions.stream().map(Direction::getId).collect(Collectors.toList()));

        // 获取课程预置课程的信息
        List<CourseDTO> courseDTOS = courseService.listCourse(
                Lists.newArrayList(CourseStatusEnum.SHARED.getCode()), true, true, null,
                courseExtends.stream().map(CourseExtend::getCourseId).collect(Collectors.toList()));

        // 转换
        return convertZtreeDTO(directions, courseExtends, courseDTOS);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createDirection(Integer type, List<DirectionParam> params) {

        // 检查参数 并 设置序号
        checkAndSetDirection(type, params);

        // 获取某类型的专业方向
        List<Direction> directions = queryDirection(Lists.newArrayList(type), null);

        // 修改数据
        List<Long> directionIds = directions.stream().map(Direction::getId).collect(Collectors.toList());
        List<DirectionParam> updateData = params.stream()
                .filter(directionParam -> directionIds.contains(directionParam.getId()))
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(updateData)) {
            // 转换数据 并修改 专业方向
            List<Direction> data = convertData(type, params);
            updateBatchDirection(data);

            // 将数据库专业方向进行过滤，为删除做准备
            List<Long> updateIds = updateData.stream().map(DirectionParam::getId).collect(Collectors.toList());
            directions = directions.stream()
                    .filter(direction -> !updateIds.contains(direction.getId())).collect(Collectors.toList());

        }

        // 删除数据库数据
        if (CollectionUtils.isNotEmpty(directions)) {
            List<Long> ids = directions.stream().map(Direction::getId).collect(Collectors.toList());
            batchDeleteDirection(ids);
        }

        // 添加数据
        List<Long> updateIds = updateData.stream().map(DirectionParam::getId).collect(Collectors.toList());
        List<DirectionParam> insertParam = params.stream()
                .filter(param -> param.getId() == null || !updateIds.contains(param.getId()))
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(insertParam)) {
            insertBatchDirection(convertData(type, insertParam));
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createCourseExtend(List<CourseDirectionParam> params) {

        // 获取课程预置课程的信息
        List<CourseDTO> presetCourses = courseService.listCourse(
                Lists.newArrayList(CourseStatusEnum.SHARED.getCode()),
                true, true, null, null);
        List<Long> courseIds = presetCourses.stream().map(CourseDTO::getId).collect(Collectors.toList());

        // 检查参数
        checkCourseDirectionParam(params, courseIds);

        // 获取引用课程
        List<CourseBasicDTO> occupyCourses = null;
        if (CollectionUtils.isNotEmpty(presetCourses)) {
            occupyCourses = courseService.listCourse(null, false, true,
                    null, null, presetCourses.stream()
                            .map(CourseDTO::getCourseBasicId).collect(Collectors.toList()));
            courseIds.addAll(occupyCourses.stream().map(CourseBasicDTO::getId).collect(Collectors.toList()));
        }

        // 删除数据库
        if (CollectionUtils.isNotEmpty(courseIds)) {
            deleteCourseExtend(null, courseIds);
        }

        // 插入
        List<CourseExtend> courseExtends = convertCourseExtend(presetCourses, occupyCourses, params);
        if (CollectionUtils.isNotEmpty(courseExtends)) {
            insertBatchCourseExtend(courseExtends);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchDeleteDirection(List<Long> ids) {
        deleteDirection(ids);
        deleteCourseExtend(ids, null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertBatchCourseExtend(List<CourseExtend> courseExtends) {
        courseExtendMapperExt.insertBatch(courseExtends);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createCourseExtend(List<Long> courseIds, List<Long> directionIds) {

        if (CollectionUtils.isEmpty(courseIds) && CollectionUtils.isEmpty(directionIds)) return;

        List<CourseExtend> courseExtends = Lists.newArrayList();

        courseIds.forEach(courseId -> {
            directionIds.forEach(directionId -> {
                CourseExtend courseExtend = new CourseExtend();
                courseExtend.setCourseId(courseId);
                courseExtend.setDirectionId(directionId);
                courseExtends.add(courseExtend);
            });
        });

        insertBatchCourseExtend(courseExtends);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertBatchDirection(List<Direction> directions) {
        directionMapperExt.insertBatch(directions);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateBatchDirection(List<Direction> directions) {
        directionMapperExt.updateBatch(directions);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void copyCourseExtend(Long courseId, Long newCourseId) {

        // 获取数据库中课程额外信息数据
        List<CourseExtend> courseExtends = queryCourseExtend(null, Lists.newArrayList(courseId));
        if (CollectionUtils.isEmpty(courseExtends)) return;

        // 添加课程方向
        createCourseExtend(
                Lists.newArrayList(newCourseId),
                courseExtends.stream().map(CourseExtend::getDirectionId).collect(Collectors.toList()));

    }

    @Override
    public List<ZtreeDTO> queryDirectionZtree(List<Integer> types, Boolean ownAuthorityFlag) {

        if (ownAuthorityFlag) {
            // 查询教师的权限
            List<Permission> permissions = userService.queryUserPermission(userInfoHolder.getUserId());
            if (CollectionUtils.isNotEmpty(permissions)) {
                types = permissions.stream().map(Permission::getStatus).distinct().collect(Collectors.toList());
            }
        }

        List<SystemConfig> configs = configService.listConfig(types, SystemConfigEnum.SCHOOL_TYPE);
        if (CollectionUtils.isEmpty(configs)) throw new NotFoundException("系统未配置学校类型");

        List<Direction> directions = queryDirection(null, null);
        if (CollectionUtils.isEmpty(directions)) throw new NotFoundException("请联系管理员配置专业方向");

        Map<Integer, List<Direction>> typeMap = BeanUtil.aggByKeyToList("type", directions);

        List<ZtreeDTO> data = Lists.newArrayList();
        configs.forEach(systemConfig -> {

            // 获取课程方向
            List<Direction> directionList = typeMap.get(systemConfig.getId());
            if (CollectionUtils.isEmpty(directionList)) return;

            ZtreeDTO ztreeDTO = new ZtreeDTO();
            ztreeDTO.setId(Long.valueOf(systemConfig.getId()));
            ztreeDTO.setName(systemConfig.getKeyValue());
            ztreeDTO.setChildren(BeanUtil.batchTransform(ZtreeDTO.class, directionList));
            data.add(ztreeDTO);
        });

        return data;

    }

    @Override
    public List<CourseExtend> queryCourseExtend(List<Long> directionIds, List<Long> courseIds) {
        CourseExtendExample example = new CourseExtendExample();
        CourseExtendExample.Criteria criteria = example.createCriteria();
        if (CollectionUtils.isNotEmpty(directionIds)) {
            criteria.andDirectionIdIn(directionIds);
        }
        if (CollectionUtils.isNotEmpty(courseIds)) {
            criteria.andCourseIdIn(courseIds);
        }
        return courseExtendMapper.selectByExample(example);
    }

    @Override
    public void deleteCourseExtend(List<Long> directionIds, List<Long> courseIds) {
        CourseExtendExample example = new CourseExtendExample();
        CourseExtendExample.Criteria criteria = example.createCriteria();
        if (CollectionUtils.isNotEmpty(directionIds)) {
            criteria.andDirectionIdIn(directionIds);
        }
        if (CollectionUtils.isNotEmpty(courseIds)) {
            criteria.andCourseIdIn(courseIds);
        }
        courseExtendMapper.deleteByExample(example);
    }

    @Override
    public List<Direction> queryDirection(List<Integer> types, List<String> names) {
        DirectionExample example = new DirectionExample();
        example.setOrderByClause("type ASC , order_num ASC");
        DirectionExample.Criteria criteria = example.createCriteria();
        if (CollectionUtils.isNotEmpty(types)) {
            criteria.andTypeIn(types);
        }
        if (CollectionUtils.isNotEmpty(names)) {
            criteria.andNameIn(names);
        }
        return directionMapper.selectByExample(example);
    }

    @Override
    public List<Direction> queryDirection(List<Long> ids) {
        DirectionExample example = new DirectionExample();
        DirectionExample.Criteria criteria = example.createCriteria();
        if (CollectionUtils.isNotEmpty(ids)) {
            criteria.andIdIn(ids);
        }
        return directionMapper.selectByExample(example);
    }

    private void checkAndSetDirection(Integer type, List<DirectionParam> params) {

        for (int i = 0; i < params.size(); i++) {
            params.get(i).setOrderNum(i + 1);
        }

        if (CollectionUtils.isEmpty(params)) return;

        // 检查type
        List<SystemConfig> configs = configService.listConfig(Lists.newArrayList(type), SystemConfigEnum.SCHOOL_TYPE);
        if (CollectionUtils.isEmpty(configs)) {
            throw new BadRequestException();
        }

        // 检查参数中名称是否重复
        List<String> names = params.stream().map(DirectionParam::getName).collect(Collectors.toList());
        HashSet<String> setData = Sets.newHashSet(names);
        if (!Objects.equals(setData.size(), names.size())) {
            throw new BadRequestException("专业方向名称不能重复");
        }

        // 检查数据库名称是否重复
        List<Direction> directions = queryDirection(null,
                params.stream().map(DirectionParam::getName).collect(Collectors.toList()));
        if (CollectionUtils.isNotEmpty(directions) && CollectionUtils.isNotEmpty(params)) {
            params.forEach(directionParam -> {
                if (directions.stream()
                        .filter(direction -> Objects.equals(direction.getName(), directionParam.getName())
                                && !Objects.equals(direction.getType(), type)).count() > 0) {
                    throw new BadRequestException("专业方向名称不能重复");
                }
            });
        }

    }

    private List<CourseExtend> queryCourseExtend(List<Long> directionIds) {
        CourseExtendExample example = new CourseExtendExample();
        CourseExtendExample.Criteria criteria = example.createCriteria();
        if (CollectionUtils.isNotEmpty(directionIds)) {
            criteria.andDirectionIdIn(directionIds);
        }
        return courseExtendMapper.selectByExample(example);
    }

    private List<ZtreeDTO> convertZtreeDTO(
            List<Direction> directions, List<CourseExtend> courseExtends, List<CourseDTO> courseDTOS) {

        Map<Long, CourseDTO> courseMap = CollectionUtils.isEmpty(courseDTOS) ?
                null : BeanUtil.mapByKey("id", courseDTOS);

        List<ZtreeDTO> ztreeDTOS = Lists.newArrayList();
        directions.forEach(direction -> {
            ZtreeDTO ztreeDTO = new ZtreeDTO();
            ztreeDTO.setId(direction.getId());
            ztreeDTO.setName(direction.getName());
            ztreeDTO.setType(direction.getType());
            ztreeDTO.setChildren(
                    convertCourseExtend(courseExtends.stream()
                            .filter(courseExtend ->
                                    Objects.equals(courseExtend.getDirectionId(), ztreeDTO.getId())
                                            && MapUtils.isNotEmpty(courseMap)
                                            && courseMap.keySet().contains(courseExtend.getCourseId()))
                            .collect(Collectors.toList()), courseMap, direction.getType())
            );
            ztreeDTOS.add(ztreeDTO);
        });

        return ztreeDTOS;

    }

    private List<ZtreeDTO> convertCourseExtend(
            List<CourseExtend> courseExtends, Map<Long, CourseDTO> courseMap, Integer type) {

        List<ZtreeDTO> ztreeDTOS = Lists.newArrayList();

        if (CollectionUtils.isEmpty(courseExtends)) return ztreeDTOS;

        courseExtends.forEach(courseExtend -> {
            ZtreeDTO ztreeDTO = new ZtreeDTO();
            ztreeDTO.setId(courseExtend.getCourseId());
            ztreeDTO.setName(courseMap.get(courseExtend.getCourseId()).getName());
            ztreeDTO.setType(type);
            ztreeDTOS.add(ztreeDTO);
        });

        return ztreeDTOS;

    }

    private void deleteDirection(List<Long> ids) {
        DirectionExample example = new DirectionExample();
        DirectionExample.Criteria criteria = example.createCriteria();
        if (CollectionUtils.isNotEmpty(ids)) {
            criteria.andIdIn(ids);
        }
        directionMapper.deleteByExample(example);
    }

    private List<Direction> convertData(Integer type, List<DirectionParam> params) {
        List<Direction> data = Lists.newArrayList();
        params.forEach(directionParam -> {
            Direction direction = new Direction();
            direction.setId(directionParam.getId());
            direction.setName(directionParam.getName());
            direction.setOrderNum(directionParam.getOrderNum());
            direction.setType(type);
            data.add(direction);
        });

        return data;
    }

    private List<CourseExtend> convertCourseExtend(
            List<CourseDTO> presetCourses, List<CourseBasicDTO> occupyCourses, List<CourseDirectionParam> params) {

        List<CourseExtend> courseExtends = Lists.newArrayList();

        // 转换
        Map<Long, List<CourseBasicDTO>> courseBasicMap = CollectionUtils.isEmpty(occupyCourses) ?
                null : BeanUtil.aggByKeyToList("courseBasicId", occupyCourses);
        Map<Long, CourseDTO> courseMap = CollectionUtils.isEmpty(presetCourses) ?
                null : BeanUtil.mapByKey("id", presetCourses);

        if (MapUtils.isEmpty(courseMap)) return null;

        params.forEach(param -> {

            Long directionId = param.getId();
            List<CourseDirectionParam> children = param.getChildren();
            if (CollectionUtils.isEmpty(children)) return;

            children.forEach(courseParam -> {

                // 获取预置课程的基本信息
                CourseDTO courseDTO = courseMap.get(courseParam.getId());
                if (courseDTO == null) return;

                // 获取预置课程+引用课程
                List<Long> courseIds = Lists.newArrayList(courseDTO.getId());
                if (MapUtils.isNotEmpty(courseBasicMap)
                        && CollectionUtils.isNotEmpty(courseBasicMap.get(courseDTO.getCourseBasicId()))) {
                    List<CourseBasicDTO> courseBasicDTOS = courseBasicMap.get(courseDTO.getCourseBasicId());
                    courseIds.addAll(courseBasicDTOS.stream().map(CourseBasicDTO::getId).collect(Collectors.toList()));
                }

                // 拼装结果集
                courseIds.forEach(courseId -> {
                    CourseExtend courseExtend = new CourseExtend();
                    courseExtend.setDirectionId(directionId);
                    courseExtend.setCourseId(courseId);
                    courseExtends.add(courseExtend);
                });

            });

        });

        return courseExtends;
    }

    private void checkCourseDirectionParam(List<CourseDirectionParam> params, List<Long> presetCourseIds) {

        // 检查专业方向是否重复
        List<Long> directionIds = params.stream()
                .map(CourseDirectionParam::getId).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(directionIds)) {
            List<Direction> directions = queryDirection(directionIds);
            if (!Objects.equals(directions.size(), directionIds.size())) {
                throw new BadRequestException();
            }
        }

        // 检查课程是否存在
        params.forEach(param -> {
            List<CourseDirectionParam> children = param.getChildren();
            if (CollectionUtils.isNotEmpty(children)
                    && children.stream()
                    .filter(courseDirectionParam -> !presetCourseIds.contains(courseDirectionParam.getId()))
                    .count() > 0) {
                throw new BadRequestException();
            }
        });

    }

    @Override
    public void download(Long directionId) {

        // 获取课程与专业方向绑定关系
        List<CourseExtend> courseExtends = queryCourseExtend(Lists.newArrayList(directionId), null);
        if (CollectionUtils.isEmpty(courseExtends)) throw new NotFoundException("暂无课程与该专业方向绑定");

        // 获取课程
        List<CourseBasicDTO> courseDTOS = courseService.listCourse(
                courseExtends.stream().map(CourseExtend::getCourseId).collect(Collectors.toList()),
                true, true, CourseStatusEnum.SHARED.getCode(), null, null);
        if (CollectionUtils.isEmpty(courseDTOS)) throw new NotFoundException("暂无课程与该专业方向绑定");

        // 获取专业方向信息
        Direction direction = directionMapper.selectByPrimaryKey(directionId);
        if (direction == null) throw new NotFoundException("专业方向不存在");
        List<SystemConfig> configs = configService.listConfig(
                Lists.newArrayList(direction.getType()), SystemConfigEnum.SCHOOL_TYPE);
        if (CollectionUtils.isEmpty(configs)) throw new BadRequestException();

        // 转换并下载
        convertDownload(courseDTOS, configs.get(0).getKeyValue() + "-" + direction.getName());

    }


    private void convertDownload(List<CourseBasicDTO> courseDTOS, String fileName) {

        List<List<Map<String, Object>>> excelList = new ArrayList<>();
        List<Map<String, Object>> list1 = Lists.newArrayList();
        Map<String, Object> headerMap1 = Maps.newHashMap();
        headerMap1.put("sheetName", "汇总记录");
        list1.add(headerMap1);
        excelList.add(list1);

        // 设置excel一些参数并下载
        List<String[]> columnNames = new ArrayList<String[]>() {
            {
                add(new String[]{"序号", "课程名称", "理论课数量", "实验课数量", "总课数"});
            }
        };
        List<String[]> keys = new ArrayList<String[]>() {
            {
                add(new String[]{"orderNumber", "courseName", "theoryCount", "experimentCount", "sumCount"});
            }
        };

        Integer theorySum = 0;
        Integer experimentSum = 0;
        Integer sum = 0;
        List<Table<Integer, Integer, String>> tableList = Lists.newArrayList();
        for (int i = 0; i < courseDTOS.size(); i++) {
            CourseBasicDTO courseDTO = courseDTOS.get(i);

            // 查询课程下的章节及任务
            List<ChapterBasicInfoDTO> chapter = chapterService.listChapterInfo(
                    Lists.newArrayList(courseDTO.getId()), null, null);
            if (CollectionUtils.isEmpty(chapter)) {
                // 设置课程汇总数据
                convertDownloadList(i + 1, courseDTO.getName(), 0, 0,
                        0, list1);
                continue;
            }

            // 设置table
            Table<Integer, Integer, String> table = HashBasedTable.create();
            table.put(0, 0, "本课简介");
            table.put(0, 1, courseDTO.getIntroduction());
            tableList.add(table);

            // 获取单元配置
            List<CourseSortDTO> courseSortDTOS = null;
            try {
                courseSortDTOS = chapterService.listChapterCourseSort(courseDTO.getId());
            } catch (NotFoundException e) {

            }

            // 获取章节
            List<ChapterBasicInfoDTO> chapters = ChapterEnum.CHAPTER.getConvertData(chapter);

            // 获取任务
            List<ChapterBasicInfoDTO> tasks = chapter.stream()
                    .filter(info -> info.getParentId() != null && info.getParentId() > 0)
                    .collect(Collectors.toList());
            Map<Long, List<ChapterBasicInfoDTO>> taskMap = BeanUtil.aggByKeyToList("parentId", tasks);

            // 获取镜像分配情况
            Map<Long, ImageAllocationTableVO> imageMap = null;
            if (CollectionUtils.isNotEmpty(tasks)) {
                imageMap = imageAllocationService.getTable(
                        ContextTypeEnum.TASK.getCode(),
                        chapter.stream().map(ChapterBasicInfoDTO::getId).collect(Collectors.toList()));
            }

            // 计算理论课和实验课数量
            List<Integer> count = getCount(chapters, taskMap);
            Integer theoryCount = count.get(0);
            Integer experimentCount = count.get(1);
            theorySum += theoryCount;
            experimentSum += experimentCount;
            sum = sum + theoryCount + experimentCount;

            // 设置课程汇总数据
            convertDownloadList(i + 1, courseDTO.getName(), theoryCount, experimentCount,
                    theoryCount + experimentCount, list1);

            // 设置课程sheet
            List<Map<String, Object>> list2 = Lists.newArrayList();
            Map<String, Object> headerMap2 = Maps.newHashMap();
            headerMap2.put("sheetName", courseDTO.getName());
            list2.add(headerMap2);
            columnNames.add(new String[]{"课程顺序", "单元名称", "章节名称", "名称", "类型",
                    "章节简介", "镜像类型", "镜像名称", "配置"});
            keys.add(new String[]{"orderNumber", "unitName", "chapterName", "name", "type",
                    "introduction", "imageType", "imageName", "configuration"});

            // 有单元情况
            if (CollectionUtils.isNotEmpty(courseSortDTOS)) {
                for (CourseSortDTO courseSortDTO : courseSortDTOS) {
                    if (CollectionUtils.isEmpty(courseSortDTO.getChapterList())) continue;

                    // 插入章节和任务
                    convertChapter(courseSortDTO.getChapterList(), taskMap,
                            courseSortDTO, list2, list2.size(), imageMap);

                    // 将插入的数据进行删除
                    List<Long> chapterIds = courseSortDTO.getChapterList().stream()
                            .map(ChapterBasicInfoDTO::getId).collect(Collectors.toList());
                    chapters = chapters.stream()
                            .filter(chapterBasicInfoDTO -> !chapterIds.contains(chapterBasicInfoDTO.getId()))
                            .collect(Collectors.toList());
                }
            }

            // 无单元情况
            if (CollectionUtils.isNotEmpty(chapters)) {
                convertChapter(chapters, taskMap, null, list2, list2.size(), imageMap);
            }

            // 数据全部插入到最终集合中
            excelList.add(list2);

        }

        // 设置sheet1中的总数
        convertDownloadList(null, null, theorySum, experimentSum, sum, list1);
        excelList.set(0, list1);

        // 设置行标和table
        List<Integer> rowNums = Lists.newArrayList();
        List<Table<Integer, Integer, String>> tables = Lists.newArrayList();
        tables.add(HashBasedTable.create());
        tables.addAll(tableList);
        for (int i = 0; i < excelList.size(); i++) {
            if (i == 0) {
                rowNums.add(0);
            } else {
                rowNums.add(1);
            }
        }


        // 导出excel
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        ExcelsUtil.ExcelOutputStream(excelList, keys, columnNames, fileName, rowNums, tables, request, response);
    }

    private List<Integer> getCount(List<ChapterBasicInfoDTO> chapters, Map<Long, List<ChapterBasicInfoDTO>> taskMap) {
        Integer theoryCount = 0;
        Integer experimentCount = 0;
        for (ChapterBasicInfoDTO chapterInfo : chapters) {
            if (ChapterTypeEnum.isContainsTheory(chapterInfo.getType())) {
                theoryCount++;
            }
            if (ChapterTypeEnum.isContainsExperiment(chapterInfo.getType())) {
                if (ChapterTypeEnum.isManyExperiment(chapterInfo.getType())) {
                    if (MapUtils.isNotEmpty(taskMap)
                            && CollectionUtils.isNotEmpty(taskMap.get(chapterInfo.getId()))) {
                        experimentCount += taskMap.get(chapterInfo.getId()).size();
                    }
                } else {
                    experimentCount++;
                }
            }
        }

        return Lists.newArrayList(theoryCount, experimentCount);
    }

    private void convertDownloadList(Integer orderNum, String courseName, Integer theoryCount,
                                     Integer experimentCount, Integer sumCount, List<Map<String, Object>> list) {
        Map<String, Object> mapValue = Maps.newHashMap();
        mapValue.put("orderNumber", orderNum == null ? "" : orderNum);
        mapValue.put("courseName", courseName == null ? "" : courseName);
        mapValue.put("theoryCount", theoryCount == null ? "" : theoryCount);
        mapValue.put("experimentCount", experimentCount == null ? "" : experimentCount);
        mapValue.put("sumCount", sumCount == null ? "" : sumCount);
        list.add(mapValue);
    }

    private void convertChapter(List<ChapterBasicInfoDTO> chapterInfos, Map<Long, List<ChapterBasicInfoDTO>> taskMap,
                                CourseSortDTO courseSortDTO, List<Map<String, Object>> list2, Integer num,
                                Map<Long, ImageAllocationTableVO> imageMap) {

        for (ChapterBasicInfoDTO chapterInfo : chapterInfos) {

            // 插入章节
            ChapterTypeEnum typeEnum = ChapterTypeEnum.getByCode(chapterInfo.getType());

            if (ChapterTypeEnum.isContainsTheory(chapterInfo.getType())) {
                convertChapterDownloadList(num, courseSortDTO == null ? "" : courseSortDTO.getName(),
                        chapterInfo.getName(), chapterInfo.getName(), typeEnum, chapterInfo.getIntroduction(),
                        "", "", "", list2);
                num++;
            }

            // 插入任务
            if (ChapterTypeEnum.isContainsExperiment(chapterInfo.getType())
                    && MapUtils.isNotEmpty(taskMap)
                    && CollectionUtils.isNotEmpty(taskMap.get(chapterInfo.getId()))) {

                for (ChapterBasicInfoDTO task : taskMap.get(chapterInfo.getId())) {

                    // 获取虚机配置信息
                    String imageType = "";
                    String imageName = "";
                    String configuration = "";
                    if (MapUtils.isNotEmpty(imageMap) && imageMap.get(task.getId()) != null) {
                        ImageAllocationTableVO vmInfo = imageMap.get(task.getId());
                        if (StringUtils.isNotBlank(vmInfo.getMode())) {
                            imageType = vmInfo.getMode();
                        }
                        if (CollectionUtils.isNotEmpty(vmInfo.getImages())) {
                            imageName = StringUtils.join(vmInfo.getImages(), "\r\n");
                        }
                        if (CollectionUtils.isNotEmpty(vmInfo.getFlavors())) {
                            configuration = StringUtils.join(vmInfo.getFlavors(), "\r\n");
                        }
                    }
                    convertChapterDownloadList(num, courseSortDTO == null ? "" : courseSortDTO.getName(),
                            chapterInfo.getName(), task.getName(), typeEnum,
                            ChapterTypeEnum.isContainsTheory(chapterInfo.getType()) ? "" : chapterInfo.getIntroduction(),
                            imageType, imageName, configuration, list2);
                    num++;
                }

            }

        }
    }

    private void convertChapterDownloadList(Integer num, String unitName, String chapterName,
                                            String name, ChapterTypeEnum typeEnum, String introduction,
                                            String imageType, String imageName, String configuration,
                                            List<Map<String, Object>> list) {
        Map<String, Object> chapterValue = Maps.newHashMap();
        chapterValue.put("orderNumber", num);
        chapterValue.put("unitName", unitName == null ? "" : unitName);
        chapterValue.put("chapterName", chapterName == null ? "" : chapterName);
        chapterValue.put("name", name == null ? "" : name);
        chapterValue.put("type", typeEnum == null ? "" : typeEnum.getMessage());
        chapterValue.put("introduction", introduction == null ? "" : introduction);
        chapterValue.put("imageType", imageType == null ? "" : imageType);
        chapterValue.put("imageName", imageName == null ? "" : imageName);
        chapterValue.put("configuration", configuration == null ? "" : configuration);
        list.add(chapterValue);
    }

}
